package com.rtx.api.utils;


import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.util.StringUtils;

import com.rtx.api.annotation.Exclude;
import com.rtx.api.annotation.SearchEqual;
import com.rtx.api.annotation.Select;
import com.rtx.api.annotation.SelectQty;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 配置示例：
 * <bean id="objectUtil" class="com.utils.ObjectUtil">
 * <property name="mapping">
 * <map>
 * <entry key="" value="${}"></entry>
 * <entry key="" value=""></entry>
 * </map>
 * </property>
 * </bean>
 * 相同的属性名的属性不需要在配置,可以注入属性名、各种类型的值、!interfacekey(基于当前时间的唯一值)、!getdate(当前时间)
 *
 * @author Zy
 * @2016年7月19日
 */
public class ObjectUtils {

    private Map<String, String> mapping;

    private List<String> src = new ArrayList<String>();

    public static Object mapToObject(Map<String, Object> map, Class<?> beanClass) throws Exception {
        if (map == null) {
            return null;
        }
        Object obj = beanClass.newInstance();
        BeanUtils.populate(obj, map);
        return obj;
    }

    /**
     * 拆分字符
     *
     * @param object
     */
    public String splitChar(Object object) {

        Class<?> cls = object.getClass();
        Field[] fields = getFieldsAndSuperClassFields(cls);
        String chars = "";

        for (Field field : fields) {
            //排序属性不作为查询条件 分页属性不为查询条件
            if (field.getAnnotation(Exclude.class) == null && !"configureSorting".equals(field.getName()) && !"pageNo".equals(field.getName()) && !"pageSize".equals(field.getName()) && !"totalPage".equals(field.getName()) && !"totalSqlText".equals(field.getName())) {
                String typeName = field.getType().getSimpleName();
                String name = field.getName();
                String methodName = getMethodName("get", name);
                Object obj = invokeMethod(methodName, cls, object, null, field.getType());

                // WJX ADD 2018/5/14 如果传过来的是已经拼接好的sql字段：spiltChars，就不需要走下面的流程
                if (field.getName().equals("spiltChars") && obj != null) {
                    chars += obj;
                    continue;
                }

                // WJX ADD 2018/5/14
                if (obj != null && !"".equals(obj)) {
                    if (DatabaseClassUtils.IsMySql()) {
                        if (obj.toString().contains("'")) {
                            obj = obj.toString().replace("'", "\\'");
                        }
                    }
                    if ("Date".equals(typeName)) {
                        if (DatabaseClassUtils.isOracle()) {
                            String str = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(obj);
                            chars += " and instr(" + field.getName() + ",to_date('" + str + "','yyyy-mm-dd hh24:mi:ss')) > 0";
                        } else if (DatabaseClassUtils.IsMySql()) {
                            String str2 = new SimpleDateFormat("yyyy-MM-dd").format(obj);
                            chars += " and instr(" + field.getName() + ",str_to_date('" + str2 + "','%Y-%m-%d')) > 0";
                        }
                    }
                    if ("String".equals(typeName)) {

                        if (field.getAnnotation(SearchEqual.class) != null) {
                            chars += " and " + field.getName() + "=" + "'" + obj + "'";
                        } else if (field.getAnnotation(Select.class) != null) {
                            String str = obj.toString();
                            String starttime = "";
                            String endtime = "";
                            if (str.indexOf(",") != -1) {
                                starttime = str.split(",")[0];
                                endtime = str.split(",")[1];
                            } else {
                                starttime = str;
                            }
                            if (!StringUtils.isEmpty(starttime)) {
                                if (DatabaseClassUtils.isOracle()) {
                                    chars += " and " + field.getAnnotation(Select.class).name() + ">=to_date('" + starttime + "','yyyy-mm-dd HH:mi:ss')  ";
                                }
                                if (DatabaseClassUtils.IsMySql()) {
                                    chars += " and " + field.getAnnotation(Select.class).name() + ">=date_format('" + starttime + "','%Y-%m-%d %H:%i:%s')  ";
                                }
                            }

                            if (!StringUtils.isEmpty(endtime)) {
                                if (DatabaseClassUtils.isOracle()) {
                                    chars += " and " + field.getAnnotation(Select.class).name() + "<=to_date('" + endtime + "','yyyy-mm-dd HH:mi:ss')  ";
                                }

                                if (DatabaseClassUtils.IsMySql()) {
                                    chars += " and " + field.getAnnotation(Select.class).name() + "<=date_format('" + endtime + "','%Y-%m-%d %H:%i:%s')  ";
                                }
                            }
                        } else if (field.getAnnotation(SelectQty.class) != null) {
                            String value = (String) obj;
                            if (value.matches("^[<>][=]*\\s*\\S+$")) {
                                // <  > <= >=  条件
                                Matcher oper = Pattern.compile("^[<>][=]*").matcher(value);
                                String operstr = "";
                                if (oper.find()) {
                                    operstr = oper.group();
                                }
                                String val = value.replaceAll("^[<>][=]*", "").trim();
                                chars += " and " + field.getAnnotation(SelectQty.class).name() + operstr + "'" + val + "'";
                            } else {
                                chars += " and " + field.getAnnotation(SelectQty.class).name() + "=" + "'" + obj + "'";
                            }
                        } else {
                            chars += " and instr(" + field.getName() + ",'" + obj + "') > 0";
                        }
                    }
                    if ("List".equals(typeName)) {
                        List<String> list = (List) obj;
                        if (list != null && list.size() > 0) {
                            String str = "('" + list.stream().collect(Collectors.joining("','")) + "')";
                            chars += " and " + field.getAnnotation(Select.class).name() + " in " + str;
                        }
                    }
                    if (!obj.toString().equals("0") && ("int".equals(typeName) || "Integer".equals(typeName))) {
                        chars += " and " + field.getName() + "=" + obj;
                    }
                    if ("Long".equals(typeName)) {
                        chars += " and " + field.getName() + "=" + obj;
                    }
                    if ("Float".equals(typeName)) {
                        chars += " and " + field.getName() + "=" + obj;
                    }
                    if ("Double".equals(typeName)) {
                        chars += " and " + field.getName() + "=" + obj;
                    }
                    if ("BigDecimal".equals(typeName)) {
                        chars += " and " + field.getName() + "=" + obj;
                    }
                }
            }
        }
        return chars;
    }

    /**
     * 拆分字符
     *
     * @param object
     * @param typeAliases
     * @return
     */
    public String splitChar(Object object, String typeAliases) {

        Class<?> cls = object.getClass();
        Field[] fields = getFieldsAndSuperClassFields(cls);
        String chars = "";
        for (Field field : fields) {
            if (field.getAnnotation(Exclude.class) == null) {
                String typeName = field.getType().getSimpleName();
                Object obj = invokeMethod(getMethodName("get", field.getName()), cls, object, null, field.getType());
                if (obj != null && !"".equals(obj)) {
                    if ("Date".equals(typeName)) {
                        String str = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(obj);

                        if (DatabaseClassUtils.isOracle()) {
                            chars += " and instr(" + typeAliases + field.getName() + ",to_date('" + str + "','yyyy-mm-dd hh24:mi:ss')) > 0";
                        } else if (DatabaseClassUtils.IsMySql()) {
                            chars += " and instr(" + typeAliases + field.getName() + ",str_to_date('" + str + "','%Y-%m-%d %H:%i:%s')) > 0";
                        }

                    }
                    if ("String".equals(typeName)) {
                        if (field.getAnnotation(SearchEqual.class) != null) {
                            chars += "and " + typeAliases + field.getName() + "=" + "'" + obj + "' ";
                        } else {
                            chars += " and instr(" + typeAliases + field.getName() + ",'" + obj + "') > 0 ";
                        }
                    }
                    if ("Int".equals(typeName) || "Integer".equals(typeName)) {
                        chars += "and " + typeAliases + field.getName() + "=" + obj;
                    }
                    if ("Long".equals(typeName)) {
                        chars += "and " + typeAliases + field.getName() + "=" + obj;
                    }
                    if ("Float".equals(typeName)) {
                        chars += "and " + typeAliases + field.getName() + "=" + obj;
                    }
                    if ("Double".equals(typeName)) {
                        chars += "and " + typeAliases + field.getName() + "=" + obj;
                    }

                }
            }
        }
        return chars;
    }

    /**
     * 复制对象
     *
     * @param srcObj
     * @param descObj
     */
    public void copyObject(Object srcObj, Object descObj) {
        Class<?> descClass = descObj.getClass();
        Class<?> srcClass = srcObj.getClass();
        Field[] descField = getFieldsAndSuperClassFields(descClass);
        parse(getFieldsAndSuperClassFields(srcClass));
        Date date = new Date();
        for (Field fields : descField) {
            if (mapping == null) {
                mapping = new HashMap<String, String>();
            }
            Class<?> type = fields.getType();
            String descName = fields.getName();
            String descMethodName = getMethodName("set", descName);
            Object returnObject = null;
            if (mapping.containsKey(descName)) {
                String value = mapping.get(descName);
                if (value.indexOf("!") == -1) {
                    String srcMethodName = getMethodName("get", value);
                    returnObject = invokeMethod(srcMethodName, srcClass, srcObj, null, type);
                } else {
                    String str = value.substring(value.indexOf("{") + 1, value.indexOf("}"));
                    if ("!interfacekey".equals(str)) {
                        returnObject = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(date);
                    } else if ("!getdate".equals(str)) {
                        returnObject = date;
                    } else {
                        returnObject = typeParse(str, type);
                    }
                }
            } else {
                if (!src.contains(descName)) {
                    continue;
                }
                String srcName = getMethodName("get", descName);
                returnObject = invokeMethod(srcName, srcClass, srcObj, null, type);
            }
            if (returnObject == null) {
                continue;
            }
            invokeMethod(descMethodName, descClass, descObj, returnObject, type);
        }
    }

    /**
     * 解析
     *
     * @param field
     */
    private void parse(Field[] field) {
        for (Field f : field) {
            src.add(f.getName());
        }
    }

    /**
     * 类型解析
     *
     * @param arg
     * @param type
     * @return
     */
    private Object typeParse(String arg, Class<?> type) {
        String typeName = type.getSimpleName();
        Object returnObj = null;
        if ("Byte".equals(typeName)) {
            returnObj = Byte.valueOf(arg);
        }
        if ("Short".equals(typeName)) {
            returnObj = Short.valueOf(arg);
        }
        if ("Int".equals(typeName) || "Integer".equals(typeName)) {
            returnObj = Integer.valueOf(arg);
        }
        if ("Long".equals(typeName)) {
            returnObj = Long.valueOf(arg);
        }
        if ("Float".equals(typeName)) {
            returnObj = Float.valueOf(arg);
        }
        if ("Double".equals(typeName)) {
            returnObj = Double.valueOf(arg);
        }
        if ("Date".equals(typeName)) {
            try {
                returnObj = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(arg);
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
        if ("String".equals(typeName)) {
            returnObj = arg;
        }
        return returnObj;
    }

    /**
     * 获取字段和超类字段
     *
     * @param cls
     * @return
     */
    private Field[] getFieldsAndSuperClassFields(Class<?> cls) {
        Field[] fields = null;
        for (; cls != Object.class; cls = cls.getSuperclass()) {
            fields = (Field[]) ArrayUtils.addAll(cls.getDeclaredFields(), fields);
        }
        return fields;
    }

    /**
     * 调用方法
     *
     * @param methodName
     * @param cla
     * @param obj
     * @param value
     * @param parameterType
     * @return
     */
    private Object invokeMethod(String methodName, Class<?> cla, Object obj, Object value, Class<?> parameterType) {
        Method method = null;
        Object returnObj = null;
        try {
            // methodName.indexOf("set") == -1
            if (value == null) {
                method = cla.getMethod(methodName);
            } else {
                method = cla.getMethod(methodName, parameterType);
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
        if (method != null) {
            try {
                if (value == null) {
                    returnObj = method.invoke(obj);
                } else {
                    returnObj = method.invoke(obj, value);
                }

            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
        return returnObj;
    }

    /**
     * 获取方法名称
     *
     * @param prefix
     * @param fieldName
     * @return
     */
    private String getMethodName(String prefix, String fieldName) {
        return prefix + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
    }

    public void setMapping(Map<String, String> mapping) {
        this.mapping = mapping;
    }

    public static boolean isNotEmpty(Collection<?> coll) {
        return !isEmpty(coll);
    }

    public static boolean isEmpty(Collection<?> coll) {
        return coll == null || coll.isEmpty();
    }

}
